//AXICare.scala
package sim

import chisel3._
import chisel3.util._
import mycore._

class AXICare extends Module {
  val io = IO(new Bundle {

    val TIME = Output(Bool())

  	val ramhelperIO = Flipped(new RAMHelperIO)
    val MYAXI = new MYAXI

    val isSoC = Input(Bool())

  })

  val TIME = RegInit(false.B)
  io.TIME := Mux(reset.asBool(), false.B, TIME)

  val WorkBoard = new WorkBoard

  val AWhandshake = io.MYAXI.AWVALID && io.MYAXI.AWREADY
  val Whandshake  = io.MYAXI.WVALID  && io.MYAXI.WREADY
  val Bhandshake  = io.MYAXI.BVALID  && io.MYAXI.BREADY
  val ARhandshake = io.MYAXI.ARVALID && io.MYAXI.ARREADY
  val Rhandshake  = io.MYAXI.RVALID  && io.MYAXI.RREADY

  /*--------------------------
      Finite-State Machine
  ---------------------------*/
  val check :: work :: time :: state_AW :: state_W :: state_B :: state_AR :: state_R :: Nil = Enum(8)
  val StateReg = RegInit(check)

  switch(StateReg){
  	is(check){
  	  WorkBoard.finished := false.B
  	  WorkBoard.en := io.ramhelperIO.en
  	  WorkBoard.rIdx := io.ramhelperIO.rIdx
  	  WorkBoard.rdata := io.ramhelperIO.rdata
  	  WorkBoard.rsize := io.ramhelperIO.rsize
  	  WorkBoard.wIdx := io.ramhelperIO.wIdx
  	  WorkBoard.wdata := io.ramhelperIO.wdata
  	  WorkBoard.wmask := io.ramhelperIO.wmask
  	  WorkBoard.wen := io.ramhelperIO.wen
  	  StateReg := work
  	}
  	is(work){
  	  when(WorkBoard.finished){
  	  	TIME := true.B
  	  	StateReg := time
  	  }.elsewhen(WorkBoard.wen){
  	  	StateReg := state_AW
  	  }.elsewhen(WorkBoard.en){
  	  	StateReg := state_AR
  	  }.otherwise{
  	  	WorkBoard.finished := true.B
  	  	StateReg := work
  	  }
  	}
  	is(time){
  	  TIME := false.B
  	  StateReg := check
  	}
  	is(state_AR){
  	  when(io.MYAXI.ARREADY){ 
  	    StateReg := state_R 
  	  }
  	}
  	is(state_R){
  	  when(Rhandshake){ 
  	    WorkBoard.rdata := io.MYAXI.RDATA
  	    WorkBoard.finished := true.B
  	    StateReg := work
  	  }
  	}
  	is(state_AW){
  	  when(AWhandshake){
  	  	StateReg := state_W
  	  }
  	}
  	is(state_W){
  	  when(Whandshake){
  	  	StateReg := state_B
  	  }
  	}
  	is(state_B){
	  when(Bhandshake){
	  	WorkBoard.finished := true.B
  	  	StateReg := work
  	  }
  	}
  }

  val WSTRB = WorkBoard.wmask
  val BitOneCnt = WSTRB(7)+ WSTRB(6)+ WSTRB(5)+ WSTRB(4)+ WSTRB(3)+ WSTRB(2)+ WSTRB(1)+ WSTRB(0)
  
  val SoC_arsize = WorkBoard.rsize
  val SoC_awsize = MuxLookup(BitOneCnt, 0.U, Array(
  	8.U -> 3.U,
  	4.U -> 2.U,
  	2.U -> 1.U,
  	1.U -> 0.U
  ))


	val ARSIZE = Mux(io.isSoC, SoC_arsize, 3.U)
	val AWSIZE = Mux(io.isSoC, SoC_awsize, 3.U)
  val ClearLowBit = Mux(io.isSoC, "hffffffffffffffff".U(64.W), "hfffffffffffffff8".U(64.W))

  when(StateReg===state_AR){

		io.MYAXI.ARVALID := true.B
		io.MYAXI.ARSIZE := ARSIZE
		io.MYAXI.ARADDR := WorkBoard.rIdx & ClearLowBit

  	io.MYAXI.AWVALID := false.B
		io.MYAXI.AWSIZE := 0.U
		io.MYAXI.AWADDR := 0.U
		io.MYAXI.WVALID := false.B
		io.MYAXI.WSTRB := 0.U
		io.MYAXI.WDATA := 0.U
		io.MYAXI.BREADY := false.B
		io.MYAXI.RREADY := false.B
  }.elsewhen(StateReg===state_R){

		io.MYAXI.RREADY := true.B

  	io.MYAXI.AWVALID := false.B
		io.MYAXI.AWSIZE := 0.U
		io.MYAXI.AWADDR := 0.U
		io.MYAXI.WVALID := false.B
		io.MYAXI.WSTRB := 0.U
		io.MYAXI.WDATA := 0.U
		io.MYAXI.BREADY := false.B
		io.MYAXI.ARVALID := false.B
		io.MYAXI.ARSIZE := ARSIZE
		io.MYAXI.ARADDR := WorkBoard.rIdx & ClearLowBit
  }.elsewhen(StateReg===state_AW){

  	io.MYAXI.AWVALID := true.B
		io.MYAXI.AWSIZE := AWSIZE
		io.MYAXI.AWADDR := WorkBoard.wIdx & ClearLowBit

		io.MYAXI.WVALID := false.B
		io.MYAXI.WSTRB := 0.U
		io.MYAXI.WDATA := 0.U
		io.MYAXI.BREADY := false.B
		io.MYAXI.ARVALID := false.B
		io.MYAXI.ARSIZE := 0.U
		io.MYAXI.ARADDR := 0.U
		io.MYAXI.RREADY := false.B
  }.elsewhen(StateReg===state_W){

		io.MYAXI.WVALID := true.B
		io.MYAXI.WSTRB := WSTRB
		io.MYAXI.WDATA := WorkBoard.wdata

  	io.MYAXI.AWVALID := false.B
		io.MYAXI.AWSIZE := AWSIZE
		io.MYAXI.AWADDR := WorkBoard.wIdx & ClearLowBit
		io.MYAXI.BREADY := false.B
		io.MYAXI.ARVALID := false.B
		io.MYAXI.ARSIZE := 0.U
		io.MYAXI.ARADDR := 0.U
		io.MYAXI.RREADY := false.B
  }.elsewhen(StateReg===state_B){

	io.MYAXI.BREADY := true.B

  	io.MYAXI.AWVALID := false.B
		io.MYAXI.AWSIZE := 0.U
		io.MYAXI.AWADDR := 0.U
		io.MYAXI.WVALID := false.B
		io.MYAXI.WSTRB := 0.U
		io.MYAXI.WDATA := 0.U
		io.MYAXI.ARVALID := false.B
		io.MYAXI.ARSIZE := 0.U
		io.MYAXI.ARADDR := 0.U
		io.MYAXI.RREADY := false.B
  }.otherwise{
  	io.MYAXI.AWVALID := false.B
		io.MYAXI.AWSIZE := 0.U
		io.MYAXI.AWADDR := 0.U
		io.MYAXI.WVALID := false.B
		io.MYAXI.WSTRB := 0.U
		io.MYAXI.WDATA := 0.U
		io.MYAXI.BREADY := false.B
		io.MYAXI.ARVALID := false.B
		io.MYAXI.ARSIZE := 0.U
		io.MYAXI.ARADDR := 0.U
		io.MYAXI.RREADY := false.B
  }

  io.ramhelperIO.rdata := WorkBoard.rdata
}

class WorkBoard {

  val finished = RegInit(false.B)

  val en = RegInit(false.B)
  val rIdx = RegInit(0.U(64.W))
  val rdata = RegInit(0.U(64.W))
  val rsize = RegInit(0.U(3.W))
  val wIdx = RegInit(0.U(64.W))
  val wdata = RegInit(0.U(64.W))
  val wmask = RegInit(0.U(8.W))
  val wen = RegInit(false.B)

}